/**
 *
 * AtJava source-code is licensed under the LGPL.
 * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
 * When copying, adapting, or redistributing this document in keeping with the guidelines above,
 * you are required to provide proper attribution to obinary.
 * If you reproduce or distribute the document without making any substantive modifications to its content,
 * please use the following attribution line:
 *
 * Copyright 2007 Atjava.com Ltd. (http://www.atjava.com) All rights reserved.
 *
 */
package com.atjava.jcrt.core;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.atjava.jcrt.BaseInfo;
import com.atjava.jcrt.core.transaction.XmlJcrtInfoDetail;
import com.atjava.jcrt.core.transaction.XmlTransactionException;
import com.atjava.jcrt.utility.DependHelper;

/**
 * 
 * @author biggie
 * @version $Revision: 1.3 $ $Date: 2008/07/11 07:17:14 $
 */
public class NodeData extends ContentHandler{
	/**
     * Logger.
     */
    private static Logger log = Logger.getLogger (NodeData.class) ;
    /**
     * Wrapped javax.jcr.Node
     */
    private Node node;
    /**
     * Empty constructor. Should NEVER be used for standard use, test only.
     */
    protected NodeData() {
        super(ItemType.NODE);
    }
    /**
     * Constructor. Create nodeData object to work-on based on existing <code>Property</code> or
     * <code>nt:resource</code>
     * @param workingNode current active <code>Node</code>
     * @param name <code>NodeData</code> name to be retrieved
     */
    /**
     * Constructor. Creates a new initialized NodeData
     * @param node <code>Node</code> of type nt:resource
     */
    public NodeData(Node node)
        throws PathNotFoundException,
        RepositoryException{
    	 super(ItemType.NODE);
        this.node = node;
    }
    /**
     * Constructor. Create nodeData object to work-on based on existing <code>Property</code> or
     * <code>nt:resource</code>
     * @param workingNode current active <code>Node</code>
     * @param name <code>NodeData</code> name to be retrieved
     */
    protected NodeData(Node workingNode, String name)
        throws PathNotFoundException,
        RepositoryException {
    	super(ItemType.NODE);
        this.node  = workingNode.getNode(name);
    }
    /**
     * Constructor. Creates a new initialized NodeData of given type
     * @param workingNode current active <code>Node</code>
     * @param name <code>NodeData</code> name to be created
     * @param createNew if true create a new Item
     * @throws PathNotFoundException
     * @throws RepositoryException
     */
    protected NodeData(Node workingNode, String name, boolean createNew)
        throws PathNotFoundException,
        RepositoryException{
    	super(ItemType.NODE);
        if (createNew) {
        	try{
        		this.node = workingNode.getNode(name);
        	}catch(PathNotFoundException e){
        		this.node = workingNode.addNode(name);
        	}
        }
        else {
            this.node  = workingNode.getNode(name);
        }
    }
    /**
     * @return atom name
     */
    public String getName() {
        try {
            // check if its a nt:resource
            if (null != this.node) {
                return this.node.getName();
            }
            return StringUtils.EMPTY;
        }
        catch (Exception e) {
            log.warn("Unable to read node name for " + this.node); //$NON-NLS-1$
            return StringUtils.EMPTY;
        }
    }
    /**
     * set attribute, Object(BaseInfo)
     * @param obj
     * @throws RepositoryException
     * @throws AccessDeniedException
     * @throws UnsupportedOperationException if its not a defined node
     */
    public void setAttribute(Object obj) throws RepositoryException,
        UnsupportedOperationException {
        if (null == this.node) {
            throw new UnsupportedOperationException("undefined node ");
        }
        BaseInfo baseInfo  = (BaseInfo)obj;
        XmlJcrtInfoDetail xmlDetail = null;
        try {
			xmlDetail = baseInfo.getXmlJcrtInfoDetail();
			Iterator iter = xmlDetail.getProperties().keySet().iterator();
			while(iter.hasNext()){
				String proName = (String)iter.next();
				String colName = (String)xmlDetail.getProperties().get(proName);

				setPropertyValue(colName,DependHelper.getValue(obj, proName));
			}
			this.node.setProperty(SystemProperty.JCRT_CLASS_TYPE, obj.getClass().getName());
		} catch (XmlTransactionException e) {
			throw new RepositoryException("Unable to read xml config data for "+obj,e);
		} catch (Exception e) {
			throw new RepositoryException("Unable to set value in the object  "+obj,e);
		}
        
    }
    /**
     * 
     * @param colName
     * @param obj
     * @throws RepositoryException
     */
    private void setPropertyValue(String colName,Object obj) throws RepositoryException{
    	if(obj == null){
    		return;
    	}
    	if (obj instanceof java.lang.String)
    		this.node.setProperty(colName, (String)obj);
    	if (obj instanceof java.lang.Long)
    		this.node.setProperty(colName, (java.lang.Long)obj);
    	if (obj instanceof java.lang.Double)
    		this.node.setProperty(colName, (java.lang.Double)obj);
    	if (obj instanceof java.lang.Integer)
    		this.node.setProperty(colName, new Long((java.lang.Integer)obj));
    	if (obj instanceof java.lang.Boolean)
    		this.node.setProperty(colName, (java.lang.Boolean)obj);
    	if (obj instanceof java.io.InputStream)
    		this.node.setProperty(colName, (java.io.InputStream)obj);
    	if (obj instanceof java.util.Calendar)
    		this.node.setProperty(colName, (java.util.Calendar)obj);
    }
    

    /**
     * get all attribute names
     * @return List of attrubute names
     * @throws RepositoryException
     */
    public List getAttributeNames() throws RepositoryException {
    	List names = new ArrayList();
        if (this.node == null) {
            if (log.isDebugEnabled()) {
                log.debug("undefined node ");
            }
            return names;
        }
        PropertyIterator properties = this.node.getProperties();
        while (properties.hasNext()) {
            names.add(properties.nextProperty().getName());
        }
        return names;
    }
    /**
     * Return Info Class 
     * @param obj
     * @return
     * @throws RepositoryException
     */
    public Object changeToInfoClass(Object obj) throws RepositoryException{
        if (null == this.node) {
            throw new UnsupportedOperationException("undefined node ");
        }
        BaseInfo baseInfo  = (BaseInfo)obj;
        XmlJcrtInfoDetail xmlDetail = null;
        try {
			xmlDetail = baseInfo.getXmlJcrtInfoDetail();
			Iterator iter = xmlDetail.getProperties().keySet().iterator();
			while(iter.hasNext()){
				String proName = (String)iter.next();
				String colName = (String)xmlDetail.getProperties().get(proName);
				setObjectValue(proName,colName,obj);
			}
			setObjectValue("className",SystemProperty.JCRT_CLASS_TYPE,obj);
		} catch (XmlTransactionException e) {
			throw new RepositoryException("Unable to read xml config data for "+obj.getClass().getName(),e);
		} catch (Exception e) {
			throw new RepositoryException("Unable to set value in the object  "+obj.getClass().getName(),e);
		}
		return obj;
    }
    /**
     * 
     * @param colName
     * @param obj
     * @throws RepositoryException
     */
    private void setObjectValue(String proName,String colName,Object obj) throws RepositoryException{
    	if(obj == null){
    		return;
    	}
    	String returnType = null;
    	try {
    		returnType = DependHelper.getReturnMethod(obj, proName).getReturnType().getName();
	    	if (returnType.equals("java.lang.String"))
	    		DependHelper.dependOn(obj, proName, this.node.getProperty(colName).getString());
	    	if (returnType.equals("java.lang.Long") || returnType.equals("long"))
	    		DependHelper.dependOn(obj, proName, this.node.getProperty(colName).getLong());
	    	if (returnType.equals("java.lang.Double") || returnType.equals("double"))
	    		DependHelper.dependOn(obj, proName, this.node.getProperty(colName).getDouble());
	    	if (returnType.equals("java.lang.Integer") || returnType.equals("int"))
	    		DependHelper.dependOn(obj, proName, new Integer(String.valueOf(this.node.getProperty(colName).getLong())));
	    	if (returnType.equals("java.lang.Boolean") || returnType.equals("boolean"))
	    		DependHelper.dependOn(obj, proName, this.node.getProperty(colName).getBoolean());
	    	if (returnType.equals("java.io.InputStream"))
	    		DependHelper.dependOn(obj, proName, this.node.getProperty(colName).getStream());
	    	if (returnType.equals("java.util.Calendar"))
	    		DependHelper.dependOn(obj, proName, this.node.getProperty(colName).getDate());
		}catch (PathNotFoundException e){
			log.debug("setObjectValue PathNotFoundException :"+e.getMessage());
		} catch (Exception e) {
			throw new RepositoryException("setObjectValue faild! proName = "+proName+" colName = "+colName,e);
		}
    }
    /**
     * checks if the atom exists in the repository
     * @return boolean
     */
    public boolean isExist() {
        return (this.node != null);
    }

    /**
     * get a handle representing path relative to the content repository
     * @return String representing path (handle) of the content
     */
    public String getHandle() {
        try {
            if (null != this.node) {
                return this.node.getPath();
            }
            return StringUtils.EMPTY;
        }
        catch (RepositoryException e) {
            log.error("Failed to get handle"); //$NON-NLS-1$
            log.error(e.getMessage(), e);
            return StringUtils.EMPTY;
        }
    }

    /**
     * Persists all changes to the repository if valiation succeds
     * @throws RepositoryException
     */
    public void save() throws RepositoryException {
        this.node.getSession().save();
    }

    /**
     * Remove this path
     * @throws RepositoryException
     */
    public void delete() throws RepositoryException {
        if (null != this.node) {
            this.node.remove();
            this.node.getSession().save();
        }
        else {
            this.node.remove();
            this.node.getSession().save();
        }
    }

    /**
     * Refreshes current node keeping all changes
     * @throws RepositoryException
     * @see javax.jcr.Node#refresh(boolean)
     */
    public void refresh(boolean keepChanges) throws RepositoryException {
        this.node.refresh(keepChanges);
    }
    
    /**
     * Return Node
     * @return
     */
    public Node getNode(){
    	return this.node;
    }

}
